/** @file

  Copyright (c) 2004  - 2014, Intel Corporation. All rights reserved.<BR>
                                                                                   
  SPDX-License-Identifier: BSD-2-Clause-Patent

                                                                                   

Module Name:

  AcpiPlatformHooks.c

Abstract:

  ACPI Platform Driver Hooks

--*/

//
// Statements that include other files.
//
#include "AcpiPlatform.h"
#include "AcpiPlatformHooks.h"
#include "Platform.h"

//
// Prototypes of the various hook functions.
//
#include "AcpiPlatformHooksLib.h"

extern SYSTEM_CONFIGURATION             mSystemConfiguration;

ENHANCED_SPEEDSTEP_PROTOCOL             *mEistProtocol  = NULL;

EFI_CPU_ID_MAP              mCpuApicIdAcpiIdMapTable[MAX_CPU_NUM];

EFI_STATUS
AppendCpuMapTableEntry (
  IN EFI_ACPI_2_0_PROCESSOR_LOCAL_APIC_STRUCTURE   *AcpiLocalApic
  )
{
  BOOLEAN Added;
  UINTN   Index;

  for (Index = 0; Index < MAX_CPU_NUM; Index++) {
    if ((mCpuApicIdAcpiIdMapTable[Index].ApicId == AcpiLocalApic->ApicId) && mCpuApicIdAcpiIdMapTable[Index].Flags) {
      return EFI_SUCCESS;
    }
  }

  Added = FALSE;
  for (Index = 0; Index < MAX_CPU_NUM; Index++) {
      if (!mCpuApicIdAcpiIdMapTable[Index].Flags) {
        mCpuApicIdAcpiIdMapTable[Index].Flags           = 1;
        mCpuApicIdAcpiIdMapTable[Index].ApicId          = AcpiLocalApic->ApicId;
        mCpuApicIdAcpiIdMapTable[Index].AcpiProcessorId = AcpiLocalApic->AcpiProcessorId;
      Added = TRUE;
      break;
    }
  }

  ASSERT (Added);
  return EFI_SUCCESS;
}

UINT32
ProcessorId2ApicId (
  UINT32  AcpiProcessorId
  )
{
  UINTN Index;

  ASSERT (AcpiProcessorId < MAX_CPU_NUM);
  for (Index = 0; Index < MAX_CPU_NUM; Index++) {
    if (mCpuApicIdAcpiIdMapTable[Index].Flags && (mCpuApicIdAcpiIdMapTable[Index].AcpiProcessorId == AcpiProcessorId)) {
      return mCpuApicIdAcpiIdMapTable[Index].ApicId;
    }
  }

  return (UINT32) -1;
}

UINT8
GetProcNumberInPackage (
  IN UINT8  Package
  )
{
  UINTN Index;
  UINT8 Number;

  Number = 0;
  for (Index = 0; Index < MAX_CPU_NUM; Index++) {
    if (mCpuApicIdAcpiIdMapTable[Index].Flags && (((mCpuApicIdAcpiIdMapTable[Index].ApicId >> 0x04) & 0x01) == Package)) {
      Number++;
    }
  }

  return Number;
}

EFI_STATUS
LocateCpuEistProtocol (
  IN UINT32                           CpuIndex,
  OUT ENHANCED_SPEEDSTEP_PROTOCOL     **EistProtocol
  )
{
  UINTN                       HandleCount;
  EFI_HANDLE                  *HandleBuffer;
  ENHANCED_SPEEDSTEP_PROTOCOL *EistProt;
  UINTN                       Index;
  UINT32                      ApicId;
  EFI_STATUS                  Status;

  HandleCount = 0;
  gBS->LocateHandleBuffer (
         ByProtocol,
         &gEnhancedSpeedstepProtocolGuid,
         NULL,
         &HandleCount,
         &HandleBuffer
         );

  Index     = 0;
  EistProt  = NULL;
  Status    = EFI_NOT_FOUND;
  while (Index < HandleCount) {
    gBS->HandleProtocol (
           HandleBuffer[Index],
           &gEnhancedSpeedstepProtocolGuid,
          (VOID **) &EistProt
           );
    //
    // Adjust the CpuIndex by +1 due to the AcpiProcessorId is 1 based.
    //
    ApicId = ProcessorId2ApicId (CpuIndex+1);
    if (ApicId == (UINT32) -1) {
      break;
    }

    if (EistProt->ProcApicId == ApicId) {
      Status = EFI_SUCCESS;
      break;
    }

    Index++;
  }

  if (HandleBuffer != NULL) {
    gBS->FreePool (HandleBuffer);
  }

  if (!EFI_ERROR (Status)) {
    *EistProtocol = EistProt;
  } else {
    *EistProtocol = NULL;
  }

  return Status;
}

EFI_STATUS
PlatformHookInit (
  VOID
  )
{
  EFI_STATUS  Status;

  Status = gBS->LocateProtocol (
                  &gEnhancedSpeedstepProtocolGuid,
                  NULL,
                  (VOID **) &mEistProtocol
                  );

  ASSERT_EFI_ERROR (Status);

  return Status;
}

/**
  Called for every ACPI table found in the BIOS flash.
  Returns whether a table is active or not. Inactive tables
  are not published in the ACPI table list.

  This hook can be used to implement optional SSDT tables or
  enabling/disabling specific functionality (e.g. SPCR table)
  based on a setup switch or platform preference. In case of
  optional SSDT tables,the platform flash will include all the
  SSDT tables but will return EFI_SUCCESS only for those tables
  that need to be published.

  @param[in]  *Table         Pointer to the active table.

  @retval  EFI_SUCCESS       if the table is active.
  @retval  EFI_UNSUPPORTED   if the table is not active.

**/
EFI_STATUS
AcpiPlatformHooksIsActiveTable (
  IN OUT EFI_ACPI_COMMON_HEADER     *Table
  )
{
  EFI_ACPI_DESCRIPTION_HEADER *TableHeader;

  TableHeader = (EFI_ACPI_DESCRIPTION_HEADER *) Table;

  if (TableHeader->Signature == EFI_ACPI_2_0_STATIC_RESOURCE_AFFINITY_TABLE_SIGNATURE) {

  }

  if ((mSystemConfiguration.ENDBG2 == 0) && (CompareMem (&TableHeader->OemTableId, "INTLDBG2", 8) == 0)) {
    return EFI_UNSUPPORTED;
  }
  return EFI_SUCCESS;
}

/**
    Update the GV3 SSDT table.

    @param[in][out]  *TableHeader   The table to be set.

    @retval  EFI_SUCCESS            Returns Success.

**/
EFI_STATUS
PatchGv3SsdtTable (
  IN OUT   EFI_ACPI_DESCRIPTION_HEADER  *TableHeader
  )
{
  UINT8                       *CurrPtr;
  UINT8                       *SsdtPointer;
  UINT32                      Signature;
  UINT32                      CpuFixes;
  UINT32                      NpssFixes;
  UINT32                      SpssFixes;
  UINT32                      CpuIndex;
  UINT32                      PackageSize;
  UINT32                      NewPackageSize;
  UINT32                      AdjustSize;
  UINTN                       EntryIndex;
  UINTN                       TableIndex;
  EFI_ACPI_NAME_COMMAND       *PssTable;
  EFI_PSS_PACKAGE             *PssTableItemPtr;
  ENHANCED_SPEEDSTEP_PROTOCOL *EistProt;
  EIST_INFORMATION            *EistInfo;
  EFI_ACPI_CPU_PSS_STATE      *PssState;
  EFI_ACPI_NAMEPACK_DWORD     *NamePtr;
  //
  // Loop through the ASL looking for values that we must fix up.
  //
  NpssFixes = 0;
  SpssFixes = 0;
  CpuFixes  = 0;
  CpuIndex  = 0;
  CurrPtr   = (UINT8 *) TableHeader;

  EistProt  = NULL;
  for (SsdtPointer = CurrPtr; SsdtPointer <= (CurrPtr + ((EFI_ACPI_COMMON_HEADER *) CurrPtr)->Length); SsdtPointer++) {
    Signature = *(UINT32 *) SsdtPointer;
    switch (Signature) {

    case SIGNATURE_32 ('_', 'P', 'R', '_'):
      //
      // _CPUX ('0' to '0xF')
      //
      CpuIndex = *(SsdtPointer + 7);
      if (CpuIndex >= '0' && CpuIndex <= '9') {
        CpuIndex -= '0';
      } else {
        if (CpuIndex > '9') {
          CpuIndex -= '7';
        }
      }

      CpuFixes++;
      LocateCpuEistProtocol (CpuIndex, &EistProt);
      break;

    case SIGNATURE_32 ('D', 'O', 'M', 'N'):

      NamePtr = ACPI_NAME_COMMAND_FROM_NAMEPACK_STR (SsdtPointer);
      if (NamePtr->StartByte != AML_NAME_OP) {
        continue;
      }

      if (NamePtr->Size != AML_NAME_DWORD_SIZE) {
        continue;
      }

      NamePtr->Value = 0;

        if (mCpuApicIdAcpiIdMapTable[CpuIndex].Flags) {
          NamePtr->Value = (mCpuApicIdAcpiIdMapTable[CpuIndex].ApicId >> 0x04) & 0x01;
      }
      break;

    case SIGNATURE_32 ('N', 'C', 'P', 'U'):

      NamePtr = ACPI_NAME_COMMAND_FROM_NAMEPACK_STR (SsdtPointer);
      if (NamePtr->StartByte != AML_NAME_OP) {
        continue;
      }

      if (NamePtr->Size != AML_NAME_DWORD_SIZE) {
        continue;
      }

        NamePtr->Value = 0;
        if (mCpuApicIdAcpiIdMapTable[CpuIndex].Flags) {
          NamePtr->Value = GetProcNumberInPackage ((mCpuApicIdAcpiIdMapTable[CpuIndex].ApicId >> 0x04) & 0x01);
      }
      break;

    case SIGNATURE_32 ('N', 'P', 'S', 'S'):
    case SIGNATURE_32 ('S', 'P', 'S', 'S'):
      if (EistProt == NULL) {
        continue;
      }

      PssTable = ACPI_NAME_COMMAND_FROM_NAME_STR (SsdtPointer);
      if (PssTable->StartByte != AML_NAME_OP) {
        continue;
      }

      EistProt->GetEistTable (EistProt, &EistInfo, (VOID **) &PssState);

      AdjustSize  = PssTable->NumEntries * sizeof (EFI_PSS_PACKAGE);
      AdjustSize -= EistInfo->NumStates * sizeof (EFI_PSS_PACKAGE);
      PackageSize     = (PssTable->Size & 0xF) + ((PssTable->Size & 0xFF00) >> 4);
      NewPackageSize  = PackageSize - AdjustSize;
      PssTable->Size  = (UINT16) ((NewPackageSize & 0xF) + ((NewPackageSize & 0x0FF0) << 4));

      //
      // Set most significant two bits of byte zero to 01, meaning two bytes used.
      //
      PssTable->Size |= 0x40;

      //
      // Set unused table to Noop Code.
      //
      SetMem( (UINT8 *) PssTable + NewPackageSize + AML_NAME_PREFIX_SIZE, AdjustSize, AML_NOOP_OP);
      PssTable->NumEntries  = (UINT8) EistInfo->NumStates;
      PssTableItemPtr       = (EFI_PSS_PACKAGE *) ((UINT8 *) PssTable + sizeof (EFI_ACPI_NAME_COMMAND));

      //
      // Update the size.
      //
      for (TableIndex = 0; TableIndex < EistInfo->NumStates; TableIndex++) {
        EntryIndex                = EistInfo->NumStates - TableIndex - 1;
        PssTableItemPtr->CoreFreq = PssState[EntryIndex].CoreFrequency * PssState[EntryIndex].Control;
        PssTableItemPtr->Power    = PssState[EntryIndex].Power * 1000;
        if (PssTable->NameStr == SIGNATURE_32 ('N', 'P', 'S', 'S')) {
          PssTableItemPtr->BMLatency    = PssState[EntryIndex].BusMasterLatency;
          PssTableItemPtr->TransLatency = PssState[EntryIndex].TransitionLatency;
        } else {
          //
          // This method should be supported by SMM PPM Handler.
          //
          PssTableItemPtr->BMLatency    = PssState[EntryIndex].BusMasterLatency * 2;
          PssTableItemPtr->TransLatency = PssState[EntryIndex].TransitionLatency * 10;
        }

        PssTableItemPtr->Control  = PssState[EntryIndex].Control;
        PssTableItemPtr->Status   = PssState[EntryIndex].Status;
        PssTableItemPtr++;
      }

      if (PssTable->NameStr == SIGNATURE_32 ('N', 'P', 'S', 'S')) {
        NpssFixes++;
      } else {
        SpssFixes++;
      }

      SsdtPointer = (UINT8 *) PssTable + PackageSize;
      break;
    }
  }

  //
  // N fixes together currently.
  //
  ASSERT (CpuFixes == (UINT32) MAX_CPU_NUM);
  ASSERT (SpssFixes == NpssFixes);
  ASSERT (CpuFixes >= SpssFixes);

  return EFI_SUCCESS;
}

/**
    Update the DSDT table.

    @param[in][out]  *TableHeader   The table to be set.

    @retval  EFI_SUCCESS            Returns EFI_SUCCESS.

**/
EFI_STATUS
PatchDsdtTable (
  IN OUT   EFI_ACPI_DESCRIPTION_HEADER  *TableHeader
  )
{

  UINT8                              *CurrPtr;
  UINT8                              *DsdtPointer;
  UINT32                             *Signature;
  UINT8                              *EndPtr;
  UINT8   *Operation;
  UINT32  *Address;
  UINT16  *Size;

  //
  // Fix PCI32 resource "FIX0" -- PSYS system status area
  //
  CurrPtr = (UINT8*) &((EFI_ACPI_DESCRIPTION_HEADER*) TableHeader)[0];
  EndPtr = (UINT8*) TableHeader;
  EndPtr = EndPtr + TableHeader->Length;
  while (CurrPtr < (EndPtr-2)) {
    //
    // Removed the _S3 tag to indicate that we do not support S3. The 4th byte is blank space
    // since there are only 3 char "_S3".
    //
    if (mSystemConfiguration.AcpiSuspendState == 0) {
      //
      // For iasl compiler version 20061109.
      //
      if ((CurrPtr[0] == '_') && (CurrPtr[1] == 'S') && (CurrPtr[2] == '3') && (CurrPtr[3] == '_')) {
        break;
      }
      //
      // For iasl compiler version 20040527.
      //
      if ((CurrPtr[0] == '\\') && (CurrPtr[1] == '_') && (CurrPtr[2] == 'S') && (CurrPtr[3] == '3')) {
        break;
      }
    }
    CurrPtr++;
  }
  CurrPtr = (UINT8*) &((EFI_ACPI_DESCRIPTION_HEADER*) TableHeader)[0];
  EndPtr = (UINT8*) TableHeader;
  EndPtr = EndPtr + TableHeader->Length;
  while (CurrPtr < (EndPtr-2)) {
    //
    // For mipi dsi port select _DEP.
    //
    if (mSystemConfiguration.MipiDsi== 1) {
      //
      // For iasl compiler version 20061109.
      //
      if ((CurrPtr[0] == 'N') && (CurrPtr[1] == 'D') && (CurrPtr[2] == 'E') && (CurrPtr[3] == 'P')) {
        CurrPtr[0] = '_';
        break;
      }

    } else {
      if ((CurrPtr[0] == 'P') && (CurrPtr[1] == 'D') && (CurrPtr[2] == 'E') && (CurrPtr[3] == 'P')) {
        CurrPtr[0] = '_';
        break;
      }

    }
    CurrPtr++;
  }
  //
  // Loop through the ASL looking for values that we must fix up.
  //
  CurrPtr = (UINT8 *) TableHeader;
  for (DsdtPointer = CurrPtr; DsdtPointer <= (CurrPtr + ((EFI_ACPI_COMMON_HEADER *) CurrPtr)->Length); DsdtPointer++) {
    Signature = (UINT32 *) DsdtPointer;

    switch (*Signature) {
    //
    // GNVS operation region.
    //
    case (SIGNATURE_32 ('G', 'N', 'V', 'S')):
      //
      // Conditional match.  For Region Objects, the Operator will always be the
      // byte immediately before the specific name.  Therefore, subtract 1 to check
      // the Operator.
      //
      Operation = DsdtPointer - 1;
      if (*Operation == AML_OPREGION_OP) {
        Address   = (UINT32 *) (DsdtPointer + 6);
        *Address  = (UINT32) (UINTN) mGlobalNvsArea.Area;
        Size      = (UINT16 *) (DsdtPointer + 11);
        *Size     = sizeof (EFI_GLOBAL_NVS_AREA);
      }
      break;
    default:
      break;
    }
  }
  return EFI_SUCCESS;
}

